﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Xml;

namespace Xejen.Communication.Soap
{
    /// <summary>
    /// 表示soap协议的响应结果的转换器
    /// </summary>
    /// <creator>marc</creator>
    public class SoapResponseParser
    {
        /// <summary>
        /// soap1.1协议的默认响应命名空间
        /// </summary>
        public const string Soap11DefaultNamespace = "http://schemas.xmlsoap.org/soap/envelope/";
        /// <summary>
        /// soap1.2协议的默认响应命名空间
        /// </summary>
        public const string Soap12DefaultNamespace = "http://www.w3.org/2003/05/soap-envelope";
        /// <summary>
        /// 使用json反序列化，将转换结果为指定类型
        /// <para>
        /// 请注意，<paramref name="xpath"/>必须查找到非soap response的根节点，本方法只接受根节点的解析
        /// </para>
        /// </summary>
        /// <typeparam name="T">反序列化的类型</typeparam>
        /// <param name="soapResponse">soap请求后的响应结果</param>
        /// <param name="xpath">查找路径</param>
        /// <param name="soapNamespamce">soap命名空间</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        //public T ParseResponse<T>(string soapResponse, string xpath = "//soap:Envelope/soap:Body/*/*", string soapNamespamce = "http://schemas.xmlsoap.org/soap/envelope/")
        //    where T : class, new()
        //{
        //    XmlDocument doc = new XmlDocument();
        //    doc.LoadXml(soapResponse);

        //    XmlNodeList paramNodes = doc.SelectNodes(xpath, GetNamespaceManager(doc, soapNamespamce));

        //    if (paramNodes.Count == 1)
        //    {
        //        XmlNode valueNode = paramNodes[0];
        //        string jsonText = JsonConvert.SerializeXmlNode(valueNode);
        //        return JsonConvert.DeserializeObject<T>(jsonText);
        //    }

        //    throw new InvalidOperationException("Invalid SOAP response format.");
        //}
        public T ParseResponse<T>(string soapResponse, string xpath = "//soap:Envelope/soap:Body/*/*/*", string soapNamespamce = null) where T : class, new()
        {
            if (string.IsNullOrEmpty(soapNamespamce))
            {
                soapNamespamce = GetSoapNamespace(soapResponse);
            }

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(soapResponse);

            XmlNodeList paramNodes = doc.SelectNodes(xpath, GetNamespaceManager(doc, soapNamespamce));

            if (paramNodes.Count > 0)
            {
                T resultObject = new T();
                PropertyInfo[] properties = typeof(T).GetProperties();
                foreach (XmlNode valueNode in paramNodes)
                {
                    foreach (PropertyInfo property in properties)
                    {
                        if (valueNode.Name == property.Name)
                        {
                            property.SetValue(resultObject, Convert.ChangeType(valueNode.InnerText, property.PropertyType), null);
                        }
                    }
                }
                return resultObject;
            }

            throw new InvalidOperationException("Invalid SOAP response format.");
        }

        /// <summary>
        /// 将响应结果转为字典
        /// <para>
        /// 请注意，<paramref name="xpath"/>必须查找到非soap response的根节点，本方法只接受根节点的解析
        /// </para>
        /// </summary>
        /// <param name="soapResponse">soap请求后的响应结果</param>
        /// <param name="xpath">查找路径</param>
        /// <param name="soapNamespamce">soap命名空间</param>
        /// <returns></returns>
        public Dictionary<string, object> ParseResponse(string soapResponse, string xpath = "//soap:Envelope/soap:Body/*/*/*", string soapNamespamce = null)
        {
            if (string.IsNullOrEmpty(soapNamespamce))
            {
                soapNamespamce = GetSoapNamespace(soapResponse);
            }

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(soapResponse);

            XmlNodeList paramNodes = doc.SelectNodes(xpath, GetNamespaceManager(doc, soapNamespamce));

            var responseParams = new Dictionary<string, object>();

            foreach (XmlNode paramNode in paramNodes)
            {
                responseParams.Add(paramNode.LocalName, paramNode.InnerText);
            }

            return responseParams;
        }

        private XmlNamespaceManager GetNamespaceManager(XmlDocument doc, string soapNamespamce)
        {
            XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
            nsMgr.AddNamespace("soap", soapNamespamce);
            return nsMgr;
        }

        /// <summary>
        /// 自动判断得出Soap命名空间
        /// </summary>
        /// <param name="soapResponse"></param>
        /// <returns></returns>
        private string GetSoapNamespace(string soapResponse)
        {
            if (soapResponse.Contains(Soap12DefaultNamespace))
            {
                return Soap12DefaultNamespace;
            }

            return Soap11DefaultNamespace;
        }
    }
}
